package com.mvc.core;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.demo.entity.Comment;

/**
 * 适配器
 * 
 * @author david
 *
 */
public class HandlerAdapter {

	HandlerMapping handlerMapping;

	public Object adaptive(String uri, HttpServletRequest request, HttpServletResponse response) {
		Method method = getHandlerMapping().getUrlMethods().get(uri);//  方法.invoke( 控制层的实例 ,  实参数据)
		// 目标方法的形参列表信息
		Parameter[] parameters = method.getParameters();
		Class<?>[] parameterTypes = method.getParameterTypes();
		Object[] parameterValues = new Object[parameters.length];// 参数值数组

		// 前端传入的数据Map
		Map<String, String[]> parameterMap = request.getParameterMap();
		// 用map存放new好的参数bean
		Map<Class<?>, Object> beanMaps = new HashMap<Class<?>, Object>();

		for (int i = 0; i < parameters.length; i++) {
			String pname = parameters[i].getName();// 形参名
			Class<?> pcls = parameters[i].getType();// 形参类型
			String ptypeName = parameters[i].getType().getSimpleName();// 形参类型

			if (ptypeName.equals("HttpServletRequest")) {
				parameterValues[i] = request;
				continue;
			}
			if (ptypeName.equals("HttpServletResponse")) {
				parameterValues[i] = response;
				continue;
			}

			// 考虑参数是Bean的情况，要把前端数据组装成Bean绑定到形参
			Set<Entry<String, String[]>> set = parameterMap.entrySet();// 拿到数据Map的键值对
			for (Entry<String, String[]> entry : set) {
				String key = entry.getKey();// comment.属性名
				String[] value = entry.getValue();

				if (key.contains(".")) {// 含.的情况，说明前端有传入bean.属性这种参数
					String[] keyArr = key.split("[.]");
					String fieldName = keyArr[1];// 得到字段名
					if (key.equals(pname + "." + keyArr[1])) {
						// 通过反射调用目标bean比如Comment实例的setXXX方法，把值塞进去，组装一个Bean
						Object beanObj = null;
						try {
							if (beanMaps.get(pcls) != null) {
								beanObj = beanMaps.get(pcls);
							} else {
								beanObj = pcls.getConstructor().newInstance();// 根据bean的类型实例化
								beanMaps.put(pcls, beanObj);
							}
							// 通过反射给某个bean的某个属性设置值
							setProperty(beanObj, fieldName, parameterMap.get(key)[0]);
							parameterValues[i] = beanObj;
						} catch (Exception e) {
							System.out.println("500 initialize parameter bean exception");
							e.printStackTrace();
						}
					}
				} else {
					// 如果传入的是普通不带.的参数
					if (parameterMap.containsKey(pname)) {
						if (ptypeName.equals("String[]")) {
							parameterValues[i] = parameterMap.get(pname);
						} else {
							parameterValues[i] = convertType(ptypeName, parameterMap.get(pname)[0]);
						}
					}
				}
			}
		}

		Object result = null;
		try {
			result = method.invoke(handlerMapping.getUrlMappings().get(uri), parameterValues);// 通过反射调用某个实例的方法
		} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
			System.out.println("500 invoke call method exception");
			e.printStackTrace();
		}
		// 返回目标方法的返回值给前端控制器
		return result;
	}

	// 把前端传入的参数值从String转成实际类型
	public Object convertType(String typeName, String value) {
		Object obj = null;
		if(value.equals("")) return obj;
		if (typeName.equals("int") || typeName.equals("Integer")) {
			obj = Integer.parseInt(value);// 把String->Integer
		} else if (typeName.equals("Long")) {
			obj = Long.parseLong(value);
		} else if (typeName.equals("Boolean")) {
			obj = Boolean.parseBoolean(value);
		} else {
			obj = value;
		}
		return obj;
	}

	/**
	 * 传入一个字段名、字段值和对象：能set该属性值
	 * 
	 * @param beanObj
	 * @param fieldName
	 * @param value
	 */
	public void setProperty(Object beanObj, String fieldName, Object value) {
		Class<?> cls = beanObj.getClass();
		try {
			// 方式1 根据字段名获取字段，调用字段.set()
			Field field = cls.getDeclaredField(fieldName);
			if (field != null) {//如果指定名称的字段存在于bean中,就setXXX
				PropertyDescriptor pd = new PropertyDescriptor(fieldName, cls);
				pd.getWriteMethod().invoke(beanObj,
						convertType(field.getType().getSimpleName(), String.valueOf(value)));
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public HandlerMapping getHandlerMapping() {
		return handlerMapping;
	}

	public void setHandlerMapping(HandlerMapping handlerMapping) {
		this.handlerMapping = handlerMapping;
	}
}
